from django.db import models
from django.utils.encoding import smart_unicode, iri_to_uri
from django.utils.html import escape
from django.utils.http import urlencode
from django.utils.safestring import mark_safe
import datetime

class Filter(object):
  filters = []
  
  def __init__(self, field, model, request):
    self.field = field
  
  @classmethod
  def register(cls, test, factory):
    cls.filters.append((test, factory))
  
  @classmethod
  def create(cls, field, model, request):
    for test, factory in cls.filters:
      if test(field):
        return factory(field, model, request)
  
  def restrict(self, queryset):
    queryset = queryset.filter(**self.get_query_kwargs())
    return queryset
  
  def has_output(self):
    return True
  
  def choices(self, query_args):
    raise NotImplementedError
  
  def title(self):
    return self.field.verbose_name
  
  def get_query_kwargs(self):
    raise NotImplementedError
  
  def update_query_args(self, query_args):
    raise NotImplementedError
  
  def gen_local_args(self, query_args, params):
    query_args = query_args.copy()
    for k, v in params.items():
      if v is None:
        if k in query_args:
          del query_args[k]
      else:
        query_args[k] = v
    
    return '?%s' % urlencode(query_args)
  
  def get_output(self, query_args):
    t = []
    if self.has_output():
      t.append(u'<h3>Po %s</h3>\n<ul>\n' % escape(self.title()))
      
      for choice in self.choices(query_args):
        t.append(u'<li%s><a href="%s">%s</a></li>\n' % \
                ((choice['selected'] and ' class="selected"' or ''),
                iri_to_uri(choice['query_string']),
                choice['display']))
      t.append('</ul>\n\n')
    
    return mark_safe("".join(t))

class RelatedFilter(Filter):
  def __init__(self, field, model, request):
    super(RelatedFilter, self).__init__(field, model, request)
    
    if isinstance(field, models.ManyToManyField):
      self.lookup_title = field.rel.to._meta.verbose_name
    else:
      self.lookup_title = field.verbose_name
    
    rel_name = field.rel.get_related_field().name
    self.lookup_kwarg = '%s__%s__exact' % (field.name, rel_name)
    self.lookup_val = request.GET.get('F%s' % field.name, None)
    
    # Only include choices that are available in the database
    rev_field = field.rel.related_name
    if not rev_field:
      rev_field = model.__name__.lower()
    
    self.lookup_choices = [
      (obj.pk, unicode(obj))
      for obj in field.rel.to.objects.exclude(**{ rev_field : None })
    ]
  
  def has_output(self):
    return len(self.lookup_choices) > 0
  
  def title(self):
    return self.lookup_title
  
  def get_query_kwargs(self):
    if self.lookup_val is None:
      return {}
    
    return { self.lookup_kwarg : self.lookup_val }
  
  def update_query_args(self, query_args):
    if self.lookup_val is not None:
      query_args['F%s' % self.field.name] = self.lookup_val
  
  def choices(self, query_args):
    yield { 'selected'      : self.lookup_val is None,
            'query_string'  : self.gen_local_args(query_args, { 'F%s' % self.field.name : None }),
            'display'       : 'Vse' }
    
    for pk_val, val in self.lookup_choices:
      yield { 'selected'      : self.lookup_val == smart_unicode(pk_val),
              'query_string'  : self.gen_local_args(query_args, { 'F%s' % self.field.name : pk_val }),
              'display'       : val }

Filter.register(lambda f: bool(f.rel), RelatedFilter)

class CatchAllFilter(Filter):
  def __init__(self, field, model, request):
    super(CatchAllFilter, self).__init__(field, model, request)
    self.lookup_kwarg = field.name
    self.lookup_val = request.GET.get('F%s' % field.name, None)
    
    # Only include choices that are available in the database
    self.lookup_choices = model.objects.distinct().order_by(field.name).values(field.name)
  
  def has_output(self):
    return len(self.lookup_choices) > 0
  
  def title(self):
    return self.field.verbose_name
  
  def get_query_kwargs(self):
    if self.lookup_val is None:
      return {}
    
    return { self.lookup_kwarg : self.lookup_val }
  
  def update_query_args(self, query_args):
    if self.lookup_val is not None:
      query_args['F%s' % self.field.name] = self.lookup_val
  
  def choices(self, query_args):
    yield { 'selected'      : self.lookup_val is None,
            'query_string'  : self.gen_local_args(query_args, { 'F%s' % self.field.name : None }),
            'display'       : 'Vse' }
    
    for val in self.lookup_choices:
      val = smart_unicode(val[self.field.name])
      yield { 'selected'      : self.lookup_val == val,
              'query_string'  : self.gen_local_args(query_args, { 'F%s' % self.field.name : val }),
              'display'       : val }

Filter.register(lambda f: True, CatchAllFilter)

